package api

import (
	"net"
	"strconv"
	"time"

	"github.com/labstack/echo/v4"
	"github.com/pkg/errors"

	"gitee.com/sillyman/PingHostsWebView/models"
	"gitee.com/sillyman/PingHostsWebView/modules/config"
	"gitee.com/sillyman/PingHostsWebView/modules/db"
	"gitee.com/sillyman/PingHostsWebView/modules/mypinger"
)

// GetHosts 列出所有的主机配置
func GetHosts(c echo.Context) error {
	hosts := make([]models.HostConfig, 0, 128)

	dbConn := db.MustObtainMainDB()
	if err := dbConn.Find(&hosts).Error; err != nil {
		return newHttpErrReadDB(err)
	}
	db.ReleaseMainDB()

	return respJSON200(c, hosts)
}

func GetHost(c echo.Context) error {
	ip := net.ParseIP(c.Param("ip"))
	if ip == nil {
		return newHttpErrBadParam(errors.New("This is not a valid textual representation of an IP address"))
	}

	var host = new(models.HostConfig)
	dbConn := db.MustObtainMainDB()
	if err := dbConn.Where("ip = ?", ip.String()).First(&host).Error; err != nil {
		return newHttpErrReadDB(err)
	}
	db.ReleaseMainDB()

	return respJSON200(c, host)
}

// GetRunningHostsIPs 列出所有的主机配置
func GetRunningHostsIPs(c echo.Context) error {
	hosts := make([]models.HostConfig, 0, 128)

	dbConn := db.MustObtainMainDB()
	if err := dbConn.Where("is_running = ?", true).Find(&hosts).Error; err != nil {
		return newHttpErrReadDB(err)
	}
	db.ReleaseMainDB()

	ips := make([]string, 0, len(hosts))
	for _, h := range hosts {
		ips = append(ips, h.IP)
	}

	return respJSON200(c, ips)
}

// AddHost 添加主机配置
// 创建 初始化的 ping 总结
func AddHost(c echo.Context) error {
	var host = new(models.HostConfig)

	if err := c.Bind(host); err != nil {
		return newHttpErrBadRequest(err)
	}
	if err := c.Validate(host); err != nil {
		return newHttpErrBadParam(err)
	}

	dbConn := db.MustObtainMainDB()
	defer db.ReleaseMainDB()

	var count int64
	if err := dbConn.Model(&models.HostConfig{}).Count(&count).Error; err != nil {
		return newHttpErrReadDB(err)
	}
	if count >= int64(config.AllowMaxNumHosts) {
		return newHttpErrServer(errors.New("已经达到最大允许添加的数量"))
	}

	if err := dbConn.Create(host).Error; err != nil {
		return newHttpErrWriteDB(err)
	}

	if err := dbConn.Create(&models.PingSummary{IP: host.IP}).Error; err != nil {
		return newHttpErrWriteDB(err)
	}

	return respJSON201(c, host)
}

// UpdateHost 更新主机配置
func UpdateHost(c echo.Context) error {
	ip := net.ParseIP(c.Param("ip"))
	if ip == nil {
		return newHttpErrBadParam(errors.New("This is not a valid textual representation of an IP address"))
	}

	var host = new(models.HostConfig)

	if err := c.Bind(host); err != nil {
		return newHttpErrBadRequest(err)
	}
	if err := c.Validate(host); err != nil {
		return newHttpErrBadParam(err)
	}
	host.IP = ip.String()
	host.UpdatedAt = time.Now()

	dbConn := db.MustObtainMainDB()
	if err := dbConn.Omit("id").Where("ip = ?", ip.String()).Save(host).Error; err != nil {
		return newHttpErrWriteDB(err)
	}
	db.ReleaseMainDB()

	return respJSON201(c, host)
}

// DelHost 删除主机
// 删除主机的配置，ping总结，ping记录
func DelHost(c echo.Context) error {
	ip := net.ParseIP(c.Param("ip"))
	if ip == nil {
		return newHttpErrBadParam(errors.New("This is not a valid textual representation of an IP address"))
	}

	// 停止ping
	mypinger.StopMyPinger(ip)

	dbConn := db.MustObtainMainDB()
	defer db.ReleaseMainDB()

	if err := dbConn.Where("ip = ?", ip.String()).Delete(models.HostConfig{}).Error; err != nil {
		return newHttpErrWriteDB(err)
	}
	if err := dbConn.Where("ip = ?", ip.String()).Delete(models.PingSummary{}).Error; err != nil {
		return newHttpErrWriteDB(err)
	}
	go func() {
		time.Sleep(time.Second * 5)
		db.MustDelPingRecordDB(ip)
	}()

	return resp204(c)
}

// SetHostRunningStatus 启动或停止pinger
func SetHostRunningStatus(c echo.Context) error {
	ip := net.ParseIP(c.Param("ip"))
	if ip == nil {
		return newHttpErrBadParam(errors.New("This is not a valid textual representation of an IP address"))
	}

	isRunning, err := strconv.ParseBool(c.QueryParam("status"))
	if err != nil {
		return newHttpErrBadParam(err)
	}
	dbConn := db.MustObtainMainDB()
	if err := dbConn.Model(&models.HostConfig{}).Where("ip = ?", ip.String()).Update("is_running", isRunning).Error; err != nil {
		return newHttpErrWriteDB(err)
	}
	db.ReleaseMainDB()

	return resp202(c)
}
